﻿using System.Data;
using Moq;
using Moq.Stub;
using Nvigorate.Data;
using Nvigorate.Test.Data.Doubles;

namespace Nvigorate.Test.Data
{
    public class DataTestBase
    {
        protected const string CONN_STRING = "<Default Connection string>";
        protected const string GET_PROC_PARAMS_COMMAND = "SELECT * FROM Parameters";
        protected const string GET_ALL_PROC_COMMAND = "SELECT * FROM Procedures";

        public Mock<IDbDataParameter> CreateParameter(string name)
        {
            return CreateParameter(name, null);
        }

        public Mock<IDbDataParameter> CreateParameter(string name, object value)
        {
            var param = new Mock<IDbDataParameter>();
            param.Stub(p => p.ParameterName);
            param.Object.ParameterName = name;
            param.Stub(p => p.Value);
            param.Object.Value = value;
            return param;
        }

        protected Mock<IDbConnection> CreateConnection()
        {
            return CreateConnection(CONN_STRING);
        }

        protected Mock<IDbConnection> CreateConnection(string connectionString)
        {
            var connection = new Mock<IDbConnection>();
            connection.Stub(c => c.ConnectionString);
            connection.Object.ConnectionString = connectionString;
            return connection;
        }

        protected Mock<IDatabaseProvider> CreateProvider()
        {
            var provider = new Mock<IDatabaseProvider>();
            provider.Expect(p => p.GetTypeDeclarationFromType(typeof (int))).Returns("int");
            provider.Expect(p => p.GetTypeDeclarationFromType(typeof (string))).Returns("varchar");
            return provider;
        }

        protected Mock<IDatabaseProvider> CreateProvider(string commandText, IDbConnection connection)
        {
            var provider = CreateProvider();
            provider.Expect(prv => prv.GetConnection(connection.ConnectionString)).Returns(CreateConnection(connection.ConnectionString).Object);
            provider.Expect(prv => prv.GetCommand(commandText)).Returns(new TestDbCommand(commandText));
            
            var getProcCommand = new TestDbCommand(GET_PROC_PARAMS_COMMAND);
            getProcCommand.Parameters.Add(CreateParameter("@procName").Object);
            provider.Expect(prv => prv.GetParamsForProcedureCommand(commandText)).Returns(getProcCommand);
            var getAllProcCommand = new TestDbCommand(GET_ALL_PROC_COMMAND);
            provider.Expect(prv => prv.GetParamsForAllProceduresCommand()).Returns(getAllProcCommand);
            return provider;
        }

        protected Mock<IDatabaseProvider> CreateProvider(string commandText, IDbConnection connection, 
            IDbCommand allParamsCommand)
        {
            var provider = CreateProvider(commandText, connection);
            provider.Expect(prv => prv.GetParamsForAllProceduresCommand()).Returns(allParamsCommand);
            return provider;
        }

        protected Mock<IDatabaseCommandFactory> CreateCommandFactory(IDatabaseProvider provider)
        {
            var getProcDbCommand = new TestDbCommand(GET_PROC_PARAMS_COMMAND);
            getProcDbCommand.Parameters.Add(CreateParameter("@procName").Object);

            var getProcCommand = new TestDatabaseCommand(getProcDbCommand, provider);
            var commandFactory = new Mock<IDatabaseCommandFactory>();
            commandFactory.Expect(factory => factory.GetCommand(It.Is<IDbCommand>(c => c.CommandText == GET_PROC_PARAMS_COMMAND),
                provider)).Returns(getProcCommand);
            return commandFactory;
        }

        protected DataSet CreateAllProcsDataSet()
        {
            var procTable = new DataTable("procs");
            procTable.Columns.Add("Procedure_Name");
            
            var paramsTable = new DataTable("params");
            paramsTable.Columns.Add("Procedure_Name");

            var ds = new DataSet();
            ds.Tables.AddRange(new[] {procTable, paramsTable});

            return ds;
        }
    }
}